
/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    main.c
 * @brief   配合多个模块，通过连接mjpg_streamer服务器端，在虚拟机显示摄像头数据
 * @author  K
 * @version 0.0.1
 * @date    2021-07-26
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-08-12   | v0.0.1    | Kcode   | 主程序
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <unistd.h>           
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include "config.h"
#include "disp_manager.h"
#include "debug_manager.h"
#include "pic_operation.h"
#include "render.h"
#include "convert_manager.h"
#include "video_recv_manager.h"

PT_VIDEORECV s_video_recv_opr;
int socket_client;  /**< socket套接字 */


/*!
 * @brief  客户端接收线程函数
 * @return long 成功：总接收到的数据长度，失败：-1
 */
void* recv_video_thread(void *data)
{
	if(s_video_recv_opr->GetVideo(&socket_client, (PT_VIDEOBUF)data) < 0)
	{		
		DBG_PRINTF("can not Get_Video\n");
	}

	return data;
}

int main(int argc, char **argv)
{
	int error;
    int video_pixel_format;
    int display_pixel_format;
	
    int lcd_width;
    int lcd_height;
    int lcd_bpp;
    int topleft_x;
    int topleft_y;
	
    T_VIDEOBUF video_buf;   
    T_VIDEOBUF convert_buf;
    T_VIDEOBUF framebuf;
	
    PT_VIDEOBUF cur_video_buf;
    PT_VIDEOCONVERTOPR video_convert_opr;   /**< 存储一帧数据的信息 */
	
	pthread_t recv_video_Id;

    /*!
     * 初始化调试系统
     */
	error = DebugInit();
	if (error) {
		printf(APP_ERR"DebugInit error! File:%s Line:%d\n", __FILE__, __LINE__);
		return -1;
	}
	
	error = InitDebugChanel();
	if (error) {
		printf(APP_ERR"InitDebugChanel error! File:%s Line:%d\n", __FILE__, __LINE__);
		return -1;
	}

    /*!
     * 操作信息提示：./mjpg_streamer_client 192.168.7.0
     */
    if (argc != 2)
    {
        DebugPrint(APP_NOTICE"Usage:\n");
        DebugPrint(APP_NOTICE"%s <ip>\n", argv[0]);
        return -1;
    }
   
    /*!
     * 注册显示模块
     */
    error = DisplayInit();
    if (error)
    {
        DebugPrint(APP_ERR"DisplayInit err\n");
        return -1;
    }
    
    /* 选择显示设备 */
    SelectAndInitDefaultDispDev("crt");

    /* 获取显示屏参数 */
    GetDispResolution(&lcd_width, &lcd_height, &lcd_bpp);

    /* 获取显示屏显存 */
    GetVideoBufForDisplay(&framebuf);

    /* 获取显示器格式 */
    display_pixel_format = framebuf.pixel_format;
    
    /*!
     * 注册视频数据接收模块 
     */
    error = VideoRecvInit();
    if (error)
    {
        DebugPrint(APP_ERR"VideoInit err\n");
        return -1;
    }    

    /*!
     * 显示视频获取通道
     */
	ShowVideoRecv();

    /* 获取视频获取操作函数 */
	s_video_recv_opr = GetVideoRecv("http");

    /* 获取视频数据格式 */
	video_pixel_format = s_video_recv_opr->GetFormat();

    /*!
     * 注册转换模块
     */
    error = VideoConvertInit();
    if (error)
    {
        DebugPrint(APP_ERR"VideoConvertInit err\n");
        return -1;
    }
    
    /* 获取支持格式的转换处理结构体 */
    video_convert_opr = GetVIdeoConvertForFormats(video_pixel_format,
                                            display_pixel_format);
    if (video_convert_opr == NULL)
    {
        DebugPrint(APP_ERR"Can not support this format convert\n");
        return -1;
    }
    
    /*!
     * 与服务器端建立连接
     */
	if(s_video_recv_opr->ConnectToServer(&socket_client, argv[1]) < 0)
	{		
		DebugPrint(APP_ERR"Can not Connect_To_Server\n");
		return -1;
	}
    
    /*!
     * 发送报文给服务器，告诉需要其所发送的数据类型与要求
     */
	if(s_video_recv_opr->Init(&socket_client) < 0)
	{
		DebugPrint(APP_ERR"Can not Init\n");
		return -1;
	}

    /*!
     * 清除video_buf的数据，并为其分配存储一帧数据的内存
     */
    memset(&video_buf, 0, sizeof(T_VIDEOBUF));
    video_buf.pixel_data.PixelDatas = (unsigned char *)malloc(30000);

    /*!
     * 清除convert_buf的数据，并设置其显示格式与bpp
     */
	memset(&convert_buf, 0, sizeof(T_VIDEOBUF));
	convert_buf.pixel_format   = display_pixel_format;
    convert_buf.pixel_data.bpp = lcd_bpp;

    /* 初始化 video_buf.db 成员 */
	if(pthread_mutex_init(&video_buf.db, NULL) != 0)		
	{
		return -1;
	}
    
    /* 初始化 video_buf.db_update(条件变量) 成员 */
	if(pthread_cond_init(&video_buf.db_update, NULL) != 0)	
	{
		DBG_PRINTF("could not initialize condition variable\n");
		return -1;
	}
	
	/*！
	 * 创建获取视频数据的线程 
	 */
	pthread_create(&recv_video_Id, NULL, &recv_video_thread, &video_buf);

    /*!
     * 处理摄像头数据
     * 如果没有按键输入，则循环显示，否则退出
     */
    while(1)
    {
        /* 等待数据的更新 */
		pthread_cond_wait(&video_buf.db_update, &video_buf.db);
        cur_video_buf = &video_buf;
    
        /*!
         * 转换为RGB 
         */
        if (video_pixel_format != display_pixel_format)
        {
            error = video_convert_opr->Convert(&video_buf, &convert_buf);
			DebugPrint(APP_ERR"Convert is begin\n");
            if (error)
            {
                DebugPrint(APP_ERR"Convert for %s err\n", argv[1]);
                /*! 
                 * 由于网络的问题可能会出现一帧的数据非jpeg数据，
                 * 使用continue可忽略这种情况 
                 */
                continue; 
            }

            cur_video_buf = &convert_buf;
        }

        /* 居中显示，计算此时的左上角坐标 */
        topleft_x = (lcd_width - cur_video_buf->pixel_data.width) / 2;
        topleft_y = (lcd_height - cur_video_buf->pixel_data.height) / 2;
        
        PicMerge(topleft_x, topleft_y, &cur_video_buf->pixel_data, &framebuf.pixel_data);
    
        /*!
         * 把framebuffer的数据刷到虚拟机，显示 
         */
        FlushPixelDatasToDev(&framebuf.pixel_data);     
    }

   pthread_detach(recv_video_Id);		// 等待线程结束,以便回收它的资源
   
   return 0;
    
}

